﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;

namespace JMT.Data
{
    public class JMSolutionStore
    {
        public string Path { get; set; }

        public void Save(JMSolution solution)
        {
            SaveAs(solution, Path);
        }

        public void SaveAs(JMSolution solution, string path)
        {
            FileInfo fileInfo = new FileInfo(path);
            string filePath = path;// Path.Combine(path, string.Format("{0}.xml", solution.Name));

            if (!fileInfo.Directory.Exists)
            {
                Directory.CreateDirectory(fileInfo.Directory.FullName);
            }

            XDocument xDoc = new XDocument();
            XElement xsolution = new XElement("s");
            xsolution.Add(new XAttribute("i", solution.Id));
            xsolution.Add(new XAttribute("n", solution.Name));

            #region package list
            XElement xpakl = new XElement("pak-l");
            List<JPackage> packageList = (solution as IJContainer<JPackage>).ToList();
            foreach (var jPackage in packageList)
            {
                SavePackage(xpakl, jPackage);
            }
            xsolution.Add(xpakl);
            #endregion

            #region interface list
            XElement xinterface = new XElement("i-l");
            List<JInterface> interfaceList = (solution as IJContainer<JInterface>).ToList();
            foreach (var jInterface in interfaceList)
            {
                SaveInterface(xinterface, jInterface);
            }
            xsolution.Add(xinterface);
            #endregion

            #region class list
            XElement xclass = new XElement("c-l");
            List<JClass> classList = (solution as IJContainer<JClass>).ToList();
            foreach (var classModel in classList)
            {
                SaveClass(xclass, classModel);
            }
            xsolution.Add(xclass);
            #endregion

            #region relation list
            XElement xrl = new XElement("r-l");
            List<JRelation> relationList = (solution as IJContainer<JRelation>).ToList();
            foreach (var jRelation in relationList)
            {
                XElement xr = new XElement("r");
                xr.Add(new XAttribute("i", jRelation.Id));
                xr.Add(new XAttribute("n", jRelation.Name));
                xr.Add(new XAttribute("t", jRelation.Type));
                xr.Add(new XAttribute("from", jRelation.FromId));
                xr.Add(new XAttribute("to", jRelation.ToId));
                xrl.Add(xr);
            }
            xsolution.Add(xrl);
            #endregion

            // diagram
            #region diagram list
            XElement xdl = new XElement("d-l");
            List<JDiagram> diagramList = (solution as IJContainer<JDiagram>).ToList();
            foreach (var diagramModel in diagramList)
            {
                XElement xd = new XElement("d");
                xd.Add(new XAttribute("i", diagramModel.Id));
                xd.Add(new XAttribute("n", diagramModel.Name));
                xd.Add(new XAttribute("t", diagramModel.Type));

                XElement xel = new XElement("e-l");
                List<JElement> elementList = (diagramModel as IJContainer<JElement>).ToList();
                foreach (var elementModel in elementList)
                {
                    XElement xe = new XElement("e");
                    xe.Add(new XAttribute("i", elementModel.Id));
                    xe.Add(new XAttribute("mi", elementModel.ModelId));
                    xe.Add(new XAttribute("l", elementModel.Left));
                    xe.Add(new XAttribute("t", elementModel.Top));
                    xe.Add(new XAttribute("w", elementModel.Width));
                    xe.Add(new XAttribute("h", elementModel.Height));
                    xel.Add(xe);
                }
                xd.Add(xel);

                xdl.Add(xd);
            }
            xsolution.Add(xdl);
            #endregion

            xDoc.Add(xsolution);
            xDoc.Save(filePath);

            this.Path = filePath;
        }

        public JMSolution Load(string path)
        {
            JMSolution solution = new JMSolution();

            XDocument xDoc = XDocument.Load(path);

            XElement xs = xDoc.Element("s");
            solution.Id = new Guid(xs.Attribute("i").Value);
            solution.Name = xs.Attribute("n").Value;

            #region package list
            IEnumerable<XElement> xpakl = xs.Element("pak-l").Elements("pak");
            foreach (var xPak in xpakl)
            {
                LoadPackage(solution, xPak);
            }
            #endregion

            #region interface list
            IEnumerable<XElement> xil = xs.Element("i-l").Elements("i");
            foreach (var xi in xil)
            {
                LoadInterface(solution, xi);
            }
            #endregion

            #region class list
            IEnumerable<XElement> xcl = xs.Element("c-l").Elements("c");
            foreach (var xc in xcl)
            {
                LoadClass(solution, xc);
            }
            #endregion

            IEnumerable<XElement> xrl = xs.Element("r-l").Elements("r");
            foreach (var xr in xrl)
            {
                JRelation jRelation = new JRelation();
                jRelation.Id = new Guid(xr.Attribute("i").Value);
                jRelation.Name = xr.Attribute("n").Value;
                jRelation.Type = xr.Attribute("t").Value;
                jRelation.FromId = new Guid(xr.Attribute("from").Value);
                jRelation.ToId = new Guid(xr.Attribute("to").Value);
                solution.Add(jRelation);
            }

            #region diagram list
            IEnumerable<XElement> xdl = xDoc.Element("s").Element("d-l").Elements("d");
            foreach (var xd in xdl)
            {
                JDiagram jDiagram = new JDiagram();
                jDiagram.Id = new Guid(xd.Attribute("i").Value);
                jDiagram.Name = xd.Attribute("n").Value;
                jDiagram.Type = xd.Attribute("t").Value;

                IEnumerable<XElement> xel = xd.Element("e-l").Elements("e");
                foreach (XElement xe in xel)
                {
                    JElement jElement = new JElement();
                    jElement.Id = new Guid(xe.Attribute("i").Value);
                    jElement.ModelId = new Guid(xe.Attribute("mi").Value);
                    jElement.Left = Convert.ToDouble(xe.Attribute("l").Value);
                    jElement.Top = Convert.ToDouble(xe.Attribute("t").Value);
                    jElement.Width = Convert.ToDouble(xe.Attribute("w").Value);
                    jElement.Height = Convert.ToDouble(xe.Attribute("h").Value);
                    jDiagram.Add(jElement);
                }

                solution.Add(jDiagram);
            }
            #endregion

            this.Path = path;
            return solution;
        }

        private void SavePackage(XElement pXElement, JMT.Data.JPackage jPackage)
        {
            XElement xPak = new XElement("pak");
            xPak.Add(new XAttribute("i", jPackage.Id.ToString("N")));
            xPak.Add(new XAttribute("n", jPackage.Name));

            #region package list
            XElement xSubpakl = new XElement("pak-l");
            List<JPackage> packageList = (jPackage as IJContainer<JPackage>).ToList();
            foreach (var jSubPackage in packageList)
            {
                SavePackage(xSubpakl, jSubPackage);
            }
            xPak.Add(xSubpakl);
            #endregion

            #region interface list
            XElement xinterface = new XElement("i-l");
            List<JInterface> interfaceList = (jPackage as IJContainer<JInterface>).ToList();
            foreach (var jInterface in interfaceList)
            {
                SaveInterface(xinterface, jInterface);
            }
            xPak.Add(xinterface);
            #endregion

            #region class list
            XElement xclass = new XElement("c-l");
            List<JClass> classList = (jPackage as IJContainer<JClass>).ToList();
            foreach (var classModel in classList)
            {
                SaveClass(xclass, classModel);
            }
            xPak.Add(xclass);
            #endregion

            #region diagram list
            XElement xdl = new XElement("d-l");
            List<JDiagram> diagramList = (jPackage as IJContainer<JDiagram>).ToList();
            foreach (var diagramModel in diagramList)
            {
                XElement xd = new XElement("d");
                xd.Add(new XAttribute("i", diagramModel.Id));
                xd.Add(new XAttribute("n", diagramModel.Name));
                xd.Add(new XAttribute("t", diagramModel.Type));

                XElement xel = new XElement("e-l");
                List<JElement> elementList = (diagramModel as IJContainer<JElement>).ToList();
                foreach (var elementModel in elementList)
                {
                    XElement xe = new XElement("e");
                    xe.Add(new XAttribute("i", elementModel.Id));
                    xe.Add(new XAttribute("mi", elementModel.ModelId));
                    xe.Add(new XAttribute("l", elementModel.Left));
                    xe.Add(new XAttribute("t", elementModel.Top));
                    xe.Add(new XAttribute("w", elementModel.Width));
                    xe.Add(new XAttribute("h", elementModel.Height));
                    xel.Add(xe);
                }
                xd.Add(xel);

                xdl.Add(xd);
            }
            xPak.Add(xdl);
            #endregion
        
            pXElement.Add(xPak);
        }

        private void SaveClass(XElement pXElement, JMT.Data.JClass jClass)
        {
            XElement xc = new XElement("c");
            xc.Add(new XAttribute("i", jClass.Id));
            xc.Add(new XAttribute("n", jClass.Name));
            xc.Add(new XAttribute("t", jClass.Type));

            #region property list
            XElement xpl = new XElement("p-l");
            List<JProperty> propertyList = (jClass as IJContainer<JProperty>).ToList();
            foreach (var propModel in propertyList)
            {
                XElement xp = new XElement("p");
                xp.Add(new XAttribute("i", propModel.Id));
                xp.Add(new XAttribute("n", propModel.Name));
                xpl.Add(xp);
            }
            xc.Add(xpl);
            #endregion

            #region method list
            XElement xml = new XElement("m-l");
            List<JMethod> methodList = (jClass as IJContainer<JMethod>).ToList();
            foreach (var methodModel in methodList)
            {
                XElement xm = new XElement("m");
                xm.Add(new XAttribute("i", methodModel.Id));
                xm.Add(new XAttribute("n", methodModel.Name));
                xml.Add(xm);
            }
            xc.Add(xml);
            #endregion

            pXElement.Add(xc);
        }

        private void SaveInterface(XElement pXElement, JMT.Data.JInterface jClass)
        {
            XElement xc = new XElement("i");
            xc.Add(new XAttribute("i", jClass.Id));
            xc.Add(new XAttribute("n", jClass.Name));
            xc.Add(new XAttribute("t", jClass.Type));

            #region property list
            XElement xpl = new XElement("p-l");
            List<JProperty> propertyList = (jClass as IJContainer<JProperty>).ToList();
            foreach (var propModel in propertyList)
            {
                XElement xp = new XElement("p");
                xp.Add(new XAttribute("i", propModel.Id));
                xp.Add(new XAttribute("n", propModel.Name));
                xpl.Add(xp);
            }
            xc.Add(xpl);
            #endregion

            #region method list
            XElement xml = new XElement("m-l");
            List<JMethod> mehotdList = (jClass as IJContainer<JMethod>).ToList();
            foreach (var methodModel in mehotdList)
            {
                XElement xm = new XElement("m");
                xm.Add(new XAttribute("i", methodModel.Id));
                xm.Add(new XAttribute("n", methodModel.Name));
                xml.Add(xm);
            }
            xc.Add(xml);
            #endregion

            pXElement.Add(xc);
        }

        private void LoadPackage(IJContainer<JPackage> jContainer, XElement xPackage)
        {
            JPackage jPackage = new JPackage();
            jPackage.Id = new Guid(xPackage.Attribute("i").Value);
            jPackage.Name = xPackage.Attribute("n").Value;

            IEnumerable<XElement> xsubpakl = xPackage.Element("pak-l").Elements("pak");
            foreach (var xSubPak in xsubpakl)
            {
                LoadPackage(jPackage, xSubPak);
            }

            #region interface list
            IEnumerable<XElement> xil = xPackage.Element("i-l").Elements("i");
            foreach (var xi in xil)
            {
                LoadInterface(jPackage, xi);
            }
            #endregion

            #region class list
            IEnumerable<XElement> xcl = xPackage.Element("c-l").Elements("c");
            foreach (var xc in xcl)
            {
                LoadClass(jPackage, xc);
            }
            #endregion

            #region diagram list
            IEnumerable<XElement> xdl = xPackage.Element("d-l").Elements("d");
            foreach (var xd in xdl)
            {
                JDiagram jDiagram = new JDiagram();
                jDiagram.Id = new Guid(xd.Attribute("i").Value);
                jDiagram.Name = xd.Attribute("n").Value;
                jDiagram.Type = xd.Attribute("t").Value;

                IEnumerable<XElement> xel = xd.Element("e-l").Elements("e");
                foreach (XElement xe in xel)
                {
                    JElement jElement = new JElement();
                    jElement.Id = new Guid(xe.Attribute("i").Value);
                    jElement.ModelId = new Guid(xe.Attribute("mi").Value);
                    jElement.Left = Convert.ToDouble(xe.Attribute("l").Value);
                    jElement.Top = Convert.ToDouble(xe.Attribute("t").Value);
                    jElement.Width = Convert.ToDouble(xe.Attribute("w").Value);
                    jElement.Height = Convert.ToDouble(xe.Attribute("h").Value);
                    jDiagram.Add(jElement);
                }

                jPackage.Add(jDiagram);
            }
            #endregion

            jContainer.Add(jPackage);
        }

        private void LoadClass(IJContainer<JClass> jContainer, XElement xClass)
        {
            JClass jClass = new JClass();
            jClass.Id = new Guid(xClass.Attribute("i").Value);
            jClass.Name = xClass.Attribute("n").Value;
            jClass.Type = xClass.Attribute("t").Value;
            jContainer.Add(jClass);

            IEnumerable<XElement> xpl = xClass.Element("p-l").Elements("p");
            foreach (var xp in xpl)
            {
                JProperty jProperty = new JProperty();
                jProperty.Id = new Guid(xp.Attribute("i").Value);
                jProperty.Name = xp.Attribute("n").Value;
                jClass.Add(jProperty);
            }

            IEnumerable<XElement> xml = xClass.Element("m-l").Elements("m");
            foreach (var xm in xml)
            {
                JMethod jMethod = new JMethod();
                jMethod.Id = new Guid(xm.Attribute("i").Value);
                jMethod.Name = xm.Attribute("n").Value;
                jClass.Add(jMethod);
            }
        }

        private void LoadInterface(IJContainer<JInterface> jContainer, XElement xInterface)
        {
            JInterface jClass = new JInterface();
            jClass.Id = new Guid(xInterface.Attribute("i").Value);
            jClass.Name = xInterface.Attribute("n").Value;
            jClass.Type = xInterface.Attribute("t").Value;
            jContainer.Add(jClass);

            IEnumerable<XElement> xpl = xInterface.Element("p-l").Elements("p");
            foreach (var xp in xpl)
            {
                JProperty jProperty = new JProperty();
                jProperty.Id = new Guid(xp.Attribute("i").Value);
                jProperty.Name = xp.Attribute("n").Value;
                jClass.Add(jProperty);
            }

            IEnumerable<XElement> xml = xInterface.Element("m-l").Elements("m");
            foreach (var xm in xml)
            {
                JMethod jMethod = new JMethod();
                jMethod.Id = new Guid(xm.Attribute("i").Value);
                jMethod.Name = xm.Attribute("n").Value;
                jClass.Add(jMethod);
            }
        }
    }
}
